﻿///*
///This program is a very modest attempt at parallelizing a program created by David Cope
///called network which was written in Common Lisp. He explains the function of the program
///in the following manner:
///
///"This program operates using adaptive resonance theory (ART) based on the work of
///Gail Carpenter and Stephen Grossberg and following the general principles of
///Mark Watson in his book Common Lisp Modules. The neural network here is
///unsupervised in that it does not use models for training but rather adapts to the
///patterns its fed. In this case, we use a musical encryption as follows:
///0.0 | 0.05 | 0.1 | 0.15 | 0.2 |0.25 | 0.3 | 0.35 | 0.4 | 0.45 | 0.5 | 0.55 | 0.6 | 0.65 | 0.7 | 0.75 | 0.8 | 0.85 | 0.9 | 0.95 | 1.0
///60  |  61  | 62  |  63  |  64 | 65  | 66  |  67  | 68  |  69  | 70  |  71  | 72  |  73  | 74  |  75  | 76  |  77  |  78 |  79  |  80
///where the bottom row represents pitches and the row above it represents the
///encryption. The network pattern matches the incoming patterns and groups them according
///to similarity, composing by recombinancy using the similar patterns only. This very
///process uses that analytical capabilities of the neural network following an otherwise
///data dependent model for composition. This represents but one of the many ways in which
///neural networks can be used for composition."
///
///Translated to C# and parallelized by Kourtni Marshall

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using MPI;

namespace Cope_Network
{
    class Parallel_Network
    {
        //MPI Environment
        public static MPI.Environment env;

        //Variables for use in network
        private static int number_of_outputs = 5;	//The number of outputs to the network.
        private static int number_of_inputs = 5;	//The number of inputs to the network.
        private static float[][] input_patterns = InputPatterns.EXTREME;
        private static float[] array_1 = new float[number_of_inputs];
        private static float[] array_2 = new float[number_of_inputs];
        private static float[] array_3 = new float[number_of_inputs];
        private static float[] array_4 = new float[number_of_inputs];
        private static float[] array_5 = new float[number_of_inputs];
        private static float[] array_6 = new float[number_of_inputs];
        private static float[] array_7 = new float[number_of_inputs];
        private static float[] array_8 = new float[number_of_outputs];
        private static float[] resetVal = new float[1];
        private static float[] y = new float[number_of_outputs];
        private static bool[] reset = new bool[number_of_outputs];
        private static int[] reset_counter = new int[number_of_outputs];
        private static int[] number_of_categories = new int[number_of_outputs];
        private static float[,] wUp = new float[number_of_inputs, number_of_outputs];
        private static float[,] wDown = new float[number_of_inputs, number_of_outputs];
        public static List<object> learned_categories = new List<object>();
        private static int learning_cycle_counter = 0;
        private static int maximum_index;
        private static bool skipReset;
        private static float[] input = new float[number_of_inputs];
        private static float[] decimals =
            { 0.0f, 0.05f, 0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.35f, 0.4f, 0.45f, 0.5f, 0.55f, 0.6f, 0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 0.95f, 1.0f };
        private static List<float> decimalsList = new List<float>(decimals);
        private static int[] pitches = { 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 };
        private static List<int> pitchesList = new List<int>(pitches);

        //Model Constants
        private static readonly float A = 0.5f;
        private static readonly float B = 0.2f;
        private static readonly float C = -1.0f;
        private static readonly float D = 0.4f;
        private static readonly float E = 0.04f;
        private static readonly float ALPHA = 1.0f;
        private static readonly float THETA = 0.3f;
        private static readonly float VIGILANCE = 0.94f;
        private static readonly float RESET_THRESHOLD = 0.05f;
        private static readonly float UPLR = 0.12f;
        private static readonly float DOWNLR = 0.12f;

        //Program Functions
        /// <summary>
        /// Assumes patterns are in decimal form.
        /// </summary>
        /// <returns></returns>
        public static List<List<int>> run_neural_net()
        {
            initialize_network(5, 5);
            initialize_the_network();
            learn_the_patterns(50);
            List<int> learnedCats_second = new List<int>();
            for (int i = 0; i < learned_categories.Count; ++i)
            {
                learnedCats_second.Add((int)(((List<object>)learned_categories[i])[1]));
            }
            List<List<object>> pairs = pair(input_patterns.ToList<float[]>().ToArray(), learnedCats_second.ToArray());
            List<object> first_5_highest = firstN(5, count_highest(pairs.ToList<List<object>>()));
            var list_to_translate = new List<List<float>>();
            foreach (List<object> list in first_5_highest)
            {
                float[] flist = (float[])list[0];
                list_to_translate.Add(flist.ToList<float>());
            }

            return translate_into_events(translate_to_pitches(list_to_translate));
        }

        public static void initialize_network(int numInputs, int numOutputs)
        {
            if (numInputs != input_patterns[0].Length)
            {
                System.Console.WriteLine(
                    "ERROR: bad input to initialize-network. numInputs should have been {0} but was {1}",
                    input_patterns[0].Length, numInputs);
                Console.ReadLine();
                return;
            }

            //Reset the network

            //Define the network size
            number_of_inputs = numInputs;
            number_of_outputs = numOutputs;

            //Array Storage Allocation
            input = new float[number_of_inputs];
            array_1 = new float[number_of_inputs];
            array_2 = new float[number_of_inputs];
            array_3 = new float[number_of_inputs];
            array_4 = new float[number_of_inputs];
            array_5 = new float[number_of_inputs];
            array_6 = new float[number_of_inputs];
            array_7 = new float[number_of_inputs];
            resetVal = new float[1];
            array_8 = new float[number_of_outputs];
            reset = new bool[number_of_outputs];
            reset_counter = new int[number_of_outputs];
            number_of_categories = new int[number_of_outputs];
            wUp = new float[number_of_inputs, number_of_outputs];
            wDown = new float[number_of_inputs, number_of_outputs];

            //Global variable to remember input patterns and
            //their associated output category code for plotting
            //by function ART2-Postprocess
            learned_categories = new List<object>();
        }

        /// <summary>
        /// Floating point random numbers.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns>a random floating point number between low and high</returns>
        public static float floating_point_random(float low, float high)
        {
            var randFloat = new Random();
            return (high - low) * (float)randFloat.NextDouble() + low;
        }

        /// <summary>
        /// Finds the largest output.
        /// </summary>
        /// <returns></returns>
        public static int find_the_largest_output()
        {
            maximum_index = 0;
            float mVal = array_8[0];
            for (int i = 0; i < number_of_outputs; ++i)
            {
                if (array_8[i] > mVal && !reset[i])
                {
                    mVal = array_8[i];
                    maximum_index = i;
                }
            }
            return maximum_index;
        }

        /// <summary>
        /// Returns D if (aref y index) is the largest value in array array-8 
        /// AND (aref array-8 index) has not been reset.
        /// </summary>
        /// <param name="a"></param>
        /// <returns></returns>
        public static float check_array_value(int index)
        {
            maximum_index = find_the_largest_output();
            if (index == maximum_index && !reset[maximum_index] && array_8[maximum_index] > RESET_THRESHOLD)
                return D;
            return 0.0f;
        }

        /// <summary>
        /// Threshold function.
        /// </summary>
        /// <param name="test"></param>
        /// <returns></returns>
        public static float sigmoid_threshold_function(float test)
        {
            if (test > THETA)
                return test;
            return 0.0f;
        }

        /// <summary>
        /// L2 Norm of a vector.
        /// </summary>
        /// <param name="a"></param>
        /// <returns></returns>
        public static float vector_L2_norm(float[] vector, int vectorLength)
        {
            float sum = 0.0f;
            for (int i = 0; i < vector.Length; ++i)
            {
                sum += vector[i] * vector[i];
            }
            return (float)(Math.Sqrt(sum) + 0.001f);
        }

        /// <summary>
        /// Update F1 STM arrays.
        /// </summary>
        public static void update_F1_STM_arrays()
        {
            //Calculate array-7 from array-5 input and backwards feed back:
            for (int i = 0; i < number_of_inputs; ++i)
            {
                float sum = 0.0f;
                for (int j = 0; j < number_of_outputs; ++j)
                {
                    sum += check_array_value(j) * wDown[j, i];
                }
                array_7[i] = sum + array_5[i];
            }

            //Update array-5 using eq. 6:
            float norm = vector_L2_norm(array_3, number_of_inputs);
            for (int i = 0; i < number_of_inputs; ++i)
            {
                array_5[i] = array_3[i] / norm;
            }

            //Update array_3 using eq. 7:
            for (int i = 0; i < number_of_inputs; ++i)
            {
                array_3[i] =
                    sigmoid_threshold_function(array_2[i]
                    + (B * sigmoid_threshold_function(array_6[i])));
            }

            //Update w using eq. 8:
            for (int i = 0; i < number_of_inputs; ++i)
            {
                array_2[i] = array_1[i] / norm;
            }

            //Update array-2 using eq. 9:
            norm = vector_L2_norm(array_1, number_of_inputs) + E;
            for (int i = 0; i < number_of_inputs; ++i)
            {
                array_2[i] = array_1[i] / norm;
            }

            //Calculate reset array-4 from eq. 20:
            float max1 = -1000.0f, max2 = -1000.0f;
            for (int i = 0; i < number_of_inputs; ++i)
            {
                if (max1 < array_5[i])
                    max1 = array_5[i];
                if (max2 < array_7[i])
                    max2 = array_7[i];
            }
            max1 += 0.001f;
            max2 += 0.001f;
            for (int i = 0; i < number_of_inputs; ++i)
            {
                array_4[i] = (array_5[i] / max1) - (array_7[i] / max2);
            }
        }

        /// <summary>
        /// Updates F2 STM storage.
        /// </summary>
        public static void update_F2_STM_storage()
        {
            for (int i = 0; i < number_of_outputs; ++i)
            {
                float sum = 0.0f;
                for (int j = 0; j < number_of_inputs; ++j)
                {
                    sum += array_7[j] * wUp[j, i];
                }
                array_8[i] = sum;
                if (reset[i])
                {
                    array_8[i] = -0.1f;
                }
            }
        }

        /// <summary>
        /// Updates the weights.
        /// </summary>
        public static void update_weights()
        {
            int largest_output = find_the_largest_output();
            if (check_array_value(largest_output) > 0.02)
            {
                for (int i = 0; i < number_of_inputs; ++i)
                {
                    wDown[largest_output, i] +=
                        DOWNLR * D * (array_7[i] - wDown[largest_output, i]);
                    wUp[i, largest_output] +=
                        UPLR * D * (array_7[i] - wUp[i, largest_output]);
                }
            }
        }

        /// <summary>
        /// Competitive learning at slab F2.
        /// </summary>
        public static void competitive_learning_at_F2()
        {
            int largest_output = find_the_largest_output();
            if (array_8[largest_output] > RESET_THRESHOLD)
            {
                for (int i = 0; i < number_of_outputs; ++i)
                {
                    if (i != largest_output)
                        array_8[i] = 0.0f;
                }
            }
        }

        /// <summary>
        /// Run one full cycle.
        /// </summary>
        public static void run_one_cycle()
        {
            update_F1_STM_arrays();
            check_for_F2_reset();
            competitive_learning_at_F2();
            update_F2_STM_storage();
            update_weights();
            competitive_learning_at_F2();
        }

        /// <summary>
        /// Check for an F2 reset condition.
        /// </summary>
        public static void check_for_F2_reset()
        {
            float res = 0.0f;
            float n1 = vector_L2_norm(array_7, number_of_inputs) + E;
            if (n1 > 0.2 && !skipReset)
            {
                if (learning_cycle_counter > 1)
                {
                    if (array_8[find_the_largest_output()] > 0.25)
                        res = 3 * vector_L2_norm(array_4, number_of_inputs);
                    //was 3.0
                    skipReset = false;
                }
            }
            resetVal[0] = res;
            if (res > (1.9 - VIGILANCE))
            {
                System.Console.WriteLine(
                    "Vigilance reset =" + res + "  Learning cycle =" + learning_cycle_counter);
                maximum_index = find_the_largest_output();
                reset[maximum_index] = true;
                reset_counter[maximum_index] = 80;
                for (int i = 0; i < number_of_outputs; ++i)
                {
                    --reset_counter[i];
                    if (reset_counter[i] < 0)
                    {
                        if (reset[i])
                            skipReset = true;
                        reset[i] = false;
                    }
                }
            }
            skipReset = false;
        }

        /// <summary>
        /// Zero activations.
        /// </summary>
        public static void zero_activations()
        {
            for (int i = 0; i < number_of_inputs; ++i)
            {
                array_1[i] = 0.0f;
                array_2[i] = 0.0f;
                array_3[i] = 0.0f;
                array_4[i] = 0.0f;
                array_5[i] = 0.0f;
                array_6[i] = 0.0f;
                array_7[i] = 0.0f;
            }
            for (int i = 0; i < number_of_outputs; ++i)
            {
                array_8[i] = 0;
                reset[i] = false;
                reset_counter[i] = 0;
            }
        }

        /// <summary>
        /// Sets up a learning pattern in the input neurons.
        /// </summary>
        /// <param name="fa"></param>
        public static void set_learning_pattern(float[] input_pattern)
        {
            int length = input_pattern.Length;
            if (length != number_of_inputs)
                System.Console.WriteLine("Error in Set_Learning_Pattern input: " + input_pattern);
            else
            {
                learning_cycle_counter = 0;
                zero_activations();
                for (int i = 0; i < length; ++i)
                {
                    input[i] = input_pattern[i] + floating_point_random(-0.08f, 0.08f);
                }
            }
        }

        /// <summary>
        /// Initialize the network.
        /// </summary>
        public static void initialize_the_network()
        {
            zero_activations();
            for (int i = 0; i < number_of_outputs; ++i)
            {
                for (int j = 0; j < number_of_inputs; ++j)
                {
                    wUp[j, i] = floating_point_random(0.05f, 0.1f);
                    wDown[i, j] = floating_point_random(0.1f, 0.03f);
                }
                number_of_categories[i] = 0;
            }
        }

        /// <summary>
        /// Cycles through all training patterns once.
        /// </summary>
        /// <param name="a"></param>
        public static void learn_the_patterns(int number)
        {
            foreach (float[] input_pattern in input_patterns)
            {
                set_learning_pattern(input_pattern);
                for (int n = 0; n < number; ++n)
                {
                    ++learning_cycle_counter;
                    run_one_cycle();
                }
                var output = new List<object>();
                output.Add(array_7);
                output.Add(find_the_largest_output());
                //lock shared resource
                lock (learned_categories)
                {
                    learned_categories.Add(output);
                }
            }
        }

        /// <summary>
        /// Pairs the two args together.
        /// </summary>
        /// <param name="one"></param>
        /// <param name="two"></param>
        /// <returns></returns>
        public static List<List<object>> pair(object[] one, int[] two)
        {
            var pairs = new List<List<object>>();
            int maxIndex =
                one.Length > two.Length ? two.Length : one.Length;
            for (int i = 0; i < maxIndex; ++i)
            {
                var pair = new List<object>();
                pair.Add(one[i]);
                pair.Add(two[i]);
                pairs.Add(pair);
            }
            return pairs;
        }

        /// <summary>
        /// Transforms note patterns into decimal patterns.
        /// </summary>
        /// <param name="note_patterns"></param>
        /// <returns></returns>
        public static float[][] make_note_decimals(int[][] note_patterns)
        {
            float[][] decimal_patterns = new float[note_patterns.Length][];
            for (int i = 0; i < note_patterns.Length; ++i)
            {
                float[] pattern = new float[note_patterns[i].Length];
                for (int j = 0; j < pattern.Length; ++j)
                {
                    pattern[j] = note_patterns[i][j] * 0.01f;
                }
                decimal_patterns[i] = pattern;
            }

            return decimal_patterns;
        }

        /// <summary>
        /// Returns the first n of list.
        /// </summary>
        /// <param name="number"></param>
        /// <param name="aList"></param>
        /// <returns></returns>
        public static List<object> firstN(int number, List<object> aList)
        {
            if (number > aList.Count) { return firstN(--number, aList); }
            return aList.GetRange(0, number);
        }

        /// <summary>
        /// Transforms decimal patterns into note patterns.
        /// </summary>
        /// <param name="decimal_lists"></param>
        /// <returns></returns>
        public static List<List<int>> translate_to_pitches(List<List<float>> decimal_lists)
        {
            var translated_lists = new List<List<int>>();
            if (decimal_lists.Count == 0)
                return translated_lists;
            translated_lists.Add(translate_pitches(decimal_lists.ElementAt(0)));
            foreach (var n in translate_to_pitches(decimal_lists.GetRange(1, decimal_lists.Count - 1)))
            {
                translated_lists.Add(n);
            }

            return translated_lists;
        }

        /// <summary>
        /// Helps transform decimal patterns into note patterns.
        /// </summary>
        /// <param name="decimal_numbers"></param>
        /// <returns></returns>
        public static List<int> translate_pitches(List<float> decimal_numbers)
        {
            var translated_list = new List<int>();
            if (decimal_numbers.Count == 0)
                return translated_list;
            int test = pitchesList.ElementAt(decimalsList.IndexOf(decimal_numbers.ElementAt(0)));

            translated_list.Add(test);

            foreach (var n in translate_pitches(decimal_numbers.GetRange(1, decimal_numbers.Count - 1)))
            {
                translated_list.Add(n);
            }

            return translated_list;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pitch_lists"></param>
        /// <returns></returns>
        public static List<List<float>> translate_to_decimals(List<List<int>> pitch_lists)
        {
            var translated_lists = new List<List<float>>();
            if (pitch_lists.Count == 0)
                return translated_lists;
            translated_lists.Add(translate_decimals(pitch_lists.ElementAt(0)));
            foreach (var n in translate_to_decimals(pitch_lists.GetRange(1, pitch_lists.Count - 1)))
            {
                translated_lists.Add(n);
            }

            return translated_lists;
        }

        /// <summary>
        /// Changes pitches into 0.0 - 1.0 range decimals.
        /// </summary>
        /// <param name="pitch_numbers"></param>
        /// <returns></returns>
        public static List<float> translate_decimals(List<int> pitch_numbers)
        {
            var translated_list = new List<float>();
            if (pitch_numbers.Count == 0)
                return translated_list;
            translated_list.Add(decimalsList.ElementAt(pitchesList.IndexOf(pitch_numbers.ElementAt(0))));
            foreach (var n in translate_decimals(pitch_numbers.GetRange(1, pitch_numbers.Count - 1)))
            {
                translated_list.Add(n);
            }

            return translated_list;
        }

        /// <summary>
        /// Returns contiguous events from its pitch-lists arg.
        /// </summary>
        /// <param name="output_pitch_lists"></param>
        /// <returns></returns>
        public static List<List<int>> translate_into_events(List<List<int>> output_pitch_lists)
        {
            var contigList = new List<int>();
            for (int i = 0; i < output_pitch_lists.Count; ++i)
            {
                for (int j = 0; j < output_pitch_lists[i].Count; ++j)
                {
                    contigList.Add(output_pitch_lists[i][j]);
                }
            }

            return make_events(contigList);
        }

        /// <summary>
        /// Makes consecutive events out of the pairs of pitches in its arg.
        /// </summary>
        /// <param name="pitch_groupings"></param>
        /// <returns></returns>
        public static List<List<int>> make_events(List<int> pitch_groupings)
        {
            var output = new List<List<int>>();
            for (int i = 0, ontime = 0; i < pitch_groupings.Count; ++i, ontime += 1000)
            {
                output.Add(make_event(ontime, pitch_groupings[i], 1));
            }
            return output;
        }

        /// <summary>
        /// Creates an event based on args.
        /// </summary>
        /// <param name="ontime"></param>
        /// <param name="pitch"></param>
        /// <param name="channel"></param>
        /// <returns></returns>
        public static List<int> make_event(int ontime, int pitch, int channel)
        {
            int[] noteEvent = { ontime, pitch, 1000, channel, 90 };
            return noteEvent.ToList();
        }

        /// <summary>
        /// Returns the highest occuring pattern in its arg.
        /// </summary>
        /// <param name="lists"></param>
        /// <returns></returns>
        public static List<object> count_highest(List<List<object>> lists)
        {
            var sorted_numbers = new List<int>();
            var ol = new List<object>();
            foreach (var list in lists)
            {
                try
                {
                    ol.Add(list);
                    sorted_numbers.Add((int)list[1]);
                }
                catch (Exception e)
                {
                    System.Console.WriteLine(
                        "Error occured during count_highest function:\n" + e.Message);
                }
            }
            sorted_numbers.Sort();
            var numbers_only = sorted_numbers.Distinct().ToList(); //remove duplicates
            List<int> counts = count_them(numbers_only, sorted_numbers);
            List<int> revCounts = new List<int>(counts);
            revCounts.Sort();
            revCounts.Reverse();
            return find_all(numbers_only[counts.IndexOf(revCounts[0])],
                ol);

        }

        /// <summary>
        /// Returns the counts of its first arg in its second arg.
        /// </summary>
        /// <param name="singles"></param>
        /// <param name="numbers"></param>
        /// <returns></returns>
        public static List<int> count_them(List<int> singles, List<int> numbers)
        {
            var output = new List<int>();
            for (int i = 0; i < singles.Count; ++i)
            {
                int sum = 0;
                foreach (int number in numbers)
                {
                    if (number == singles[i])
                        ++sum;
                }
                output.Add(sum);
            }
            return output;
        }

        /// <summary>
        /// Returns all of the patterns associated with number.
        /// </summary>
        /// <param name="number"></param>
        /// <param name="list"></param>
        /// <returns></returns>
        public static List<object> find_all(int number, List<object> lists)
        {
            var output = new List<object>();
            foreach (List<object> li in lists)
            {
                try
                {
                    if ((int)li[1] == number)
                        output.Add(li);
                }
                catch (Exception e)
                {
                    System.Console.WriteLine("Exception encountered in find_all method:\n" + e.Message);
                }
            }
            return output;
        }

        /// <summary>
        /// Non-destructive sort function.
        /// </summary>
        /// <param name="function"></param>
        /// <param name="lists"></param>
        /// <returns></returns>
        public static List<int> mySort(string function, List<int> lists)
        {
            var output = new List<int>(lists);
            output.Sort();
            if (function.Equals("#<"))
                return output;
            if (function.Equals("#>"))
            {
                output.Reverse();
                return output;
            }
            System.Console.WriteLine("First param of mySort function must be '#>' or '#<'");
            return null;
        }


        private static void test()
        {
            //Console.WriteLine("The input to the nueral net is:");
            //Console.Write("( ");
            //for (int i = 0; i < input_patterns.Length; ++i)
            //{
            //    foreach (float f in input_patterns[i])
            //        Console.Write(f + " ");
            //    if (i == input_patterns.Length - 1)
            //        Console.WriteLine(")");
            //    else
            //        Console.Write(") ( ");
            //}
            //Console.WriteLine();
            //Console.WriteLine();
            Console.WriteLine("Starting Neural Network:");
            DateTime start = System.DateTime.Now;
            var nn = run_neural_net();
            Console.WriteLine("Neural Network comleted in an elapsed time of {0}", System.DateTime.Now - start);
            Console.WriteLine();
            Console.WriteLine("The output of the nueral net is:");
            Console.Write("( ");
            IEnumerable<List<int>> eList = nn.AsEnumerable();
            for (int i = 0; i < nn.Count; ++i)
            {
                foreach (int n in nn[i])
                    Console.Write(n + " ");
                if (i == nn.Count - 1)
                    Console.WriteLine(")");
                else
                    Console.Write(") ( ");
            }
        }

        public static void Main(string[] args)
        {
            using (env = new MPI.Environment(ref args))
            {
                Intracommunicator comm = Communicator.world;
                DateTime start = System.DateTime.Now;
                if (comm.Rank == 0)
                {
                    Console.WriteLine("Starting Neural Network:");
                    initialize_network(5, 5);
                    initialize_the_network();
                }
                comm.Barrier();
                learn_the_patterns(50 / comm.Size);
                comm.Barrier();
                if (comm.Rank == 0)
                {

                    List<int> learnedCats_second = new List<int>();
                    for (int i = 0; i < learned_categories.Count; ++i)
                    {
                        learnedCats_second.Add((int)(((List<object>)learned_categories[i])[1]));
                    }
                    List<List<object>> pairs = pair(input_patterns.ToList<float[]>().ToArray(), learnedCats_second.ToArray());
                    List<object> first_5_highest = firstN(5, count_highest(pairs.ToList<List<object>>()));
                    var list_to_translate = new List<List<float>>();
                    foreach (List<object> list in first_5_highest)
                    {
                        float[] flist = (float[])list[0];
                        list_to_translate.Add(flist.ToList<float>());
                    }

                    var output = translate_into_events(translate_to_pitches(list_to_translate));
                    Console.WriteLine("Neural Network comleted in an elapsed time of {0}", System.DateTime.Now - start);
                    Console.WriteLine();
                    Console.WriteLine("The output of the nueral net is:");
                    Console.Write("( ");
                    IEnumerable<List<int>> eList = output.AsEnumerable();
                    for (int i = 0; i < output.Count; ++i)
                    {
                        foreach (int n in output[i])
                            Console.Write(n + " ");
                        if (i == output.Count - 1)
                            Console.WriteLine(")");
                        else
                            Console.Write(") ( ");
                    }
                }
            }
        }
    }
}